import { KMap, KNumTable, KNumTree, KSingletonMap, KStrTable, setFromNumArray } from '@coldcloude/kai2';
import { GridForm, KKP2D, KKP2DTable, KKStepBroker } from '@coldcloude/kakera';
import { CSC } from "./CivSimConstant";
import { ACT_TURN_PREPARE, AM_NORMAL, AT_ARG, AT_CITY, AT_IND, AT_MILI, AT_RESC, calcPopularCost, calcTechLevel, CivSimAction, CivSimActionAreaBuild, CivSimActionAreaRemove, CivSimActionAreaSwitch, CivSimActionArmyAttack, CivSimActionArmyBuildCity, CivSimActionArmyMove, CivSimActionArmyOccupy, CivSimActionArmyProduct, CivSimActionMulti, CivSimActionPopularAlloc, CivSimActionPopularUnallocArea, CivSimActionPopularUnallocCity, CivSimAgent, CivSimArea, CivSimArmy, CivSimCity, CivSimCiv, CivSimCivDetail, CivSimEntity, CivSimIndustryArea, CivSimLand, CivSimMilitaryArea, CivSimModeArea, CivSimObserver, CivSimView, CMD_AREA_BLD, CMD_AREA_RM, CMD_AREA_SWITCH, CMD_ARMY_ATK, CMD_ARMY_MOV, CMD_ARMY_OCC, CMD_ARMY_PROD, CMD_POPULAR_ALLOC, CMD_POPULAR_UNALLOC_AREA, CMD_POPULAR_UNALLOC_CITY, CMD_TURN_END, CMDS } from './CivSimCommon';

function addOther<T extends CivSimEntity>(pos:KKP2D,allMap:KMap<KKP2D,T>,filter:(id:number)=>boolean,otherMap:KMap<KKP2D,T>){
    const other = allMap.get(pos);
    if(other&&filter(other.belong.id)){
        otherMap.set(pos,other);
    }
}

function addOtherMap<T extends CivSimEntity>(allMap:KMap<KKP2D,T>,filter:(id:number)=>boolean,otherMap:KMap<KKP2D,T>){
    allMap.foreach((pos,other)=>{
        if(filter(other.belong.id)){
            otherMap.set(pos,other);
        }
    });
}

export class CivSimBroker extends KKStepBroker<CivSimAgent,CivSimObserver,number,CivSimAction,CivSimView> {
    gridForm:GridForm;
    allCivMap: KMap<number,CivSimCivDetail>;
    allLandMap: KMap<KKP2D,CivSimLand>;
    allAreaMap: KMap<KKP2D,CivSimArea>;
    allArmyMap: KMap<KKP2D,CivSimArmy>;
    allCityMap: KMap<KKP2D,CivSimCity>;
    turn = 0;
    turnChanged = true;
    civIds: number[] = [];
    civAgentMap = new KNumTable<string>();
    lastCiv:CivSimCivDetail = null;
    lastAction:CivSimAction = null;
    constructor(
        agentMap: KMap<string,CivSimAgent>,
        observerMap: KMap<string,CivSimObserver>,
        gridForm: GridForm,
        allCivMap: KMap<number,CivSimCivDetail>,
        allLandMap: KMap<KKP2D,CivSimLand>,
        allAreaMap: KMap<KKP2D,CivSimArea>,
        allArmyMap: KMap<KKP2D,CivSimArmy>,
        allCityMap: KMap<KKP2D,CivSimCity>
    ){
        super(agentMap,observerMap);
        this.gridForm = gridForm;
        this.allCivMap = allCivMap;
        this.allLandMap = allLandMap;
        this.allAreaMap = allAreaMap;
        this.allArmyMap = allArmyMap;
        this.allCityMap = allCityMap;
        allCivMap.foreach((id)=>{
            this.civIds.push(id);
        });
        agentMap.foreach((id,agent)=>{
            agent.setGridForm(gridForm);
            const civId = agent.getControlId();
            this.civAgentMap.set(civId,id);
        });
        observerMap.foreach((id,ob)=>{
            ob.setGridForm(gridForm);
        });
    }
    _getCurrCiv(){
        const index = this.turn%this.civIds.length;
        return this.allCivMap.get(this.civIds[index]);
    }
    generateAgentCommandsMap(): KMap<string, number[]> {
        const rmap = new KSingletonMap(undefined,undefined);
        if(this.running&&!this.turnChanged){
            const civ = this._getCurrCiv();
            if(civ){
                const agentId = this.civAgentMap.get(civ.id)!;
                rmap.set(agentId,CMDS);
            }
        }
        return rmap;
    }
    _setLand(civ:CivSimCiv,target:CivSimLand){
        const pos = target.position;
        target.belong = civ;
        civ.landMap.set(pos,target);
        //同时获得其上area
        const targetArea = this.allAreaMap.get(pos);
        if(targetArea){
            targetArea.belong = civ;
            civ.areaMap.set(pos,targetArea);
        }
    }
    _unsetLand(civ:CivSimCiv,target:CivSimLand){
        const pos = target.position;
        target.belong = null;
        civ.landMap.get(pos,true);
        //同时移除其上area的所属
        const targetArea = this.allAreaMap.get(pos);
        if(targetArea){
            targetArea.belong = null;
            civ.areaMap.get(pos,true);
        }
    }
    _setArmy(civ:CivSimCiv,target:CivSimArmy){
        const pos = target.position;
        target.belong = civ;
        civ.armyMap.set(pos,target);
        this.allArmyMap.set(pos,target);
    }
    _unsetArmy(civ:CivSimCiv,target:CivSimArmy){
        const pos = target.position;
        target.belong = null;
        civ.armyMap.get(pos,true);
        this.allArmyMap.get(pos,true);
    }
    _addArea(civ:CivSimCiv,pos:KKP2D,type:number,area?:CivSimArea){
        //新建功能区
        if(!area){
            switch(type){
                case AT_IND:
                    area = new CivSimIndustryArea();
                    break;
                case AT_MILI:
                    area = new CivSimMilitaryArea();
                    break;
                default:
                    area = new CivSimArea();
                break;
            }
        }
        area.type = type;
        area.belong = civ;
        this.allAreaMap.set(pos,area);
        civ.areaMap.set(pos,area);
    }
    _removeArea(civ:CivSimCiv,pos:KKP2D){
        civ.areaMap.get(pos,true);
        this.allAreaMap.get(pos,true);
    }
    _addCity(civ:CivSimCiv,pos:KKP2D):boolean{
        let r = false;
        //判断周围无其他city
        const nps = this.gridForm.getAreaGrids(pos,CSC.city.mutualRadius);
        let valid = true;
        for(const np of nps){
            if(this.allCityMap.contains(np)){
                valid = false;
                break;
            }
        }
        //新建city
        if(valid){
            const city = new CivSimCity();
            this.allCityMap.set(pos,city);
            civ.cityMap.set(pos,city);
            this._addArea(civ,pos,AT_CITY,city);
            r = true;
        }
        return r;
    }
    _removeCity(civ:CivSimCiv,pos:KKP2D){
        this._removeArea(civ,pos);
        civ.cityMap.get(pos,true);
        this.allCityMap.get(pos,true);
    }
    _prepareTurn(civ:CivSimCivDetail){
        //计算功能区进度
        civ.areaMap.foreach((pos,area)=>{
            if(area.workerFrom&&area.level<area.targetLevel){
                area.progress += CSC.area.progress[area.targetLevel];
                if(area.progress>=100){
                    area.progress = 0;
                    area.level++;
                }
            }
        });
        //当城里有人工作时，恢复城防
        civ.cityMap.foreach((pos,city)=>{
            if(city.workerFrom&&city.level>0){
                city.wall = Math.min(city.wall+CSC.city.wallRenew[city.level],CSC.city.wallMax[city.level]);
            }
        })
        //计算科技、净财富、净粮食
        civ.areaMap.foreach((pos,area)=>{
            if(area.workerFrom!=null&&area.level>=0){
                //计算增长值
                let conf:number[][] = undefined;
                switch(area.type){
                    case AT_CITY:
                        conf = CSC.area.production.city;
                        break;
                    case AT_ARG:
                        conf = CSC.area.production.agriculture;
                        break;
                    case AT_IND:
                        if((area as CivSimIndustryArea).mode===AM_NORMAL){
                            conf = CSC.area.production.industry;
                        }
                        else{
                            conf = CSC.area.production.industryResearch;
                        }
                        break;
                    case AT_MILI:
                        if((area as CivSimMilitaryArea).mode===AM_NORMAL){
                            conf = CSC.area.production.militery;
                        }
                        else{
                            conf = CSC.area.production.militeryResearch;
                        }
                        break;
                    case AT_RESC:
                        conf = CSC.area.production.research;
                        break;
                    default:
                        break;
                }
                if(conf&&area.level>=0){
                    //计算粮食增长
                    const city = area.workerFrom;
                    if(city.level>=0){
                        city.grain += conf[area.level][0];
                    }
                    //计算财富增长
                    civ.gold += conf[area.level][1];
                    //分类计算科技增长
                    const dt = conf[area.level][2];
                    switch(area.type){
                        case AT_IND:
                            civ.ecoTechPoint += dt;
                            break;
                        case AT_MILI:
                            civ.armyTechPoint += dt;
                            break;
                        default:
                            civ.techPoint += dt;
                            break;
                        }
                }
            }
        });
        //科技转化
        if(civ.ecoTechPoint>0){
            const tdt = Math.ceil(civ.ecoTechPoint*CSC.tech.industryConvert)
            civ.ecoTechPoint -= tdt;
            civ.techPoint += tdt;
        }
        else if(civ.armyTechPoint>0){
            const tdt = Math.ceil(civ.armyTechPoint*CSC.tech.militaryConvert)
            civ.armyTechPoint -= tdt;
            civ.techPoint += tdt;
        }
        //根据科技计算军队等级，军队等级上升时，数量根据原有战斗力乘以一定比例重新计算
        const newArmyLevel = calcTechLevel(civ,false);
        civ.armyMap.foreach((pos,army)=>{
            if(newArmyLevel>army.level){
                const oldPower = CSC.army.power[army.level]*army.amount;
                army.amount = Math.ceil(oldPower*CSC.army.powerUpdate/CSC.army.power[newArmyLevel]);
            }    
        });
        //支付维护费，财富不足会由少到多减少军队数量
        const armyAmountMap = new KNumTable<CivSimArmy[]>();
        civ.armyMap.foreach((pos,army)=>{
            armyAmountMap.computeIfAbsent(army.amount,()=>[]).push(army);
        });
        if(civ.gold>0){
            while(armyAmountMap.size>0){
                const armys = armyAmountMap.getLast(true)!;
                while(armys.value.length>0){
                    const army = armys.value.pop()!;
                    civ.gold -= army.amount*CSC.army.cost[newArmyLevel];
                    if(civ.gold<=0){
                        break;
                    }
                }
                if(armys.value.length>0){
                    armyAmountMap.set(armys.key,armys.value);
                }
                if(civ.gold<=0){
                    break;
                }
            }
        }
        armyAmountMap.foreach((amount,armys)=>{
            for(const army of armys){
                civ.gold -= army.amount*CSC.army.cost[newArmyLevel];
                civ.armyMap.get(army.position,true);
                this.allArmyMap.get(army.position,true);
            }
        });
        //财富充足，增加军队数量
        if(civ.gold>0){
            civ.areaMap.foreach((pos,area)=>{
                if(area.type===AT_MILI){
                    const aa = area as CivSimMilitaryArea;
                    if(aa.producting&&aa.mode===AM_NORMAL){
                        aa.producting = false;//军队生产状态为一次性
                        const da = CSC.army.product[newArmyLevel];
                        civ.armyMap.compute(pos,(pa)=>{
                            if(pa===undefined){
                                const na = new CivSimArmy();
                                na.position = pos;
                                na.level = newArmyLevel;
                                this._setArmy(civ,na);
                            }
                            pa.value.amount = Math.min(CSC.army.stack,pa.value.amount+da);
                            return pa;
                        });
                    }
                }
            });
        }
        //粮食不足会按比例降低人口，粮食充足会按比例增加人口
        const popularDownCitySet = new KKP2DTable<boolean>();
        civ.cityMap.foreach((pos,city)=>{
            if(city.level>=0){
                city.grain = city.grain-city.popular+1;
                if(city.grain>0){
                    //人口增长不可超过上限
                    city.popular = Math.min(CSC.city.maxPopular[city.level],city.popular*CSC.city.popularUp);
                    //粮食存储不可超过上限
                    city.grain = Math.min(city.grain,CSC.city.maxGrain[city.level]);
                }
                else if(city.grain<0){
                    city.popular = Math.max(1,city.popular*CSC.city.popularDown);
                    popularDownCitySet.set(city.position,true);
                    city.grain = 0;
                }
            }
        })
        //人口不足时，撤回该城市所有人口，可以重新分配
        civ.areaMap.foreach((pos,area)=>{
            if(area.workerFrom){
                const city = area.workerFrom;
                if(popularDownCitySet.contains(city.position)){
                    area.workerFrom = null;
                }
            }
        })
        //计算各土地城市影响力，无主之地影响力足够自动占领，无城市影响力下降直到脱离
        civ.landMap.foreach((pos,land)=>{
            land.civiling = false;
        })
        civ.cityMap.foreach((pos,city)=>{
            const nps = this.gridForm.getAreaGrids(pos,CSC.city.civilingRadius);
            const candidatesMap = new KNumTree<CivSimLand[]>();
            const candidatesOtherMap = new KNumTree<CivSimLand[]>();
            for(const np of nps){
                const land = this.allLandMap.get(np);
                if(land){
                    if(land.belong&&land.belong.id===civ.id){
                        //属于自身的，标记为受影响的
                        land.civiling = true;
                    }
                    else{
                        //非自身的，备选，按当前影响力排序
                        const cc = land.civilingMap.get(civ.id)||0;
                        if(land.belong){
                            //无所属的备选
                            candidatesMap.computeIfAbsent(cc,()=>[]).push(land);
                        }
                        else{
                            //所属其他人的备选
                            candidatesOtherMap.computeIfAbsent(cc,()=>[]).push(land);
                        }
                    }
                }
            }
            //挑选当前影响力最高的地块继续增加影响力
            const target = candidatesMap.size===0?candidatesOtherMap.size===0?undefined:candidatesOtherMap.getLast().value[0]:candidatesMap.getLast().value[0];
            if(target){
                const civil = target.civilingMap.compute(civ.id,(entry)=>{
                    if(!entry){
                        entry = {
                            key: civ.id,
                            value: 0
                        }
                    }
                    const distance = this.gridForm.getGridDistance(pos,target.position);
                    entry.value += CSC.city.civiling(city.popular,distance);
                    if(entry.value>100){
                        entry.value = 100;
                    }
                    return entry;
                }).value;
                if(!target.belong&&civil>=100){
                    //无所属，影响力满，加入
                    this._setLand(civ,target);
                }
            }
            //降低无城市土地的影响力
            const rmTargets:CivSimLand[] = [];
            civ.landMap.foreach((pos,land)=>{
                if(!land.civiling){
                    const civil = land.civilingMap.compute(civ.id,(entry)=>{
                        if(entry){
                            entry.value -= CSC.city.civilingDown;
                            if(entry.value<0){
                                entry.value = 0;
                            }
                        }
                        return entry;
                    })!.value;
                    if(civil<=0){
                        //影响力降为0，准备脱离
                        rmTargets.push(land);
                    }
                }
            });
            //移除脱离的土地
            for(const rmt of rmTargets){
                this._unsetLand(civ,rmt);
            }
        });
        //恢复军队行动力
        civ.armyMap.foreach((pos,army)=>{
            army.movable = true;
            army.armyAttackable = true;
            army.cityAttackable = true;
        });
        //更新视野
        const newTechLevel = calcTechLevel(civ,true);
        civ.sight = CSC.tech.sight[Math.max(newArmyLevel,newTechLevel)];
    }
    _executeActionPopularUnallocArea(civ:CivSimCivDetail,action:CivSimActionPopularUnallocArea):boolean{
        //将指定area的工人退回
        let r = false;
        const area = civ.areaMap.get(action.source);
        if(area){
            area.workerFrom = null;
            r = true;
        }
        return r;
    }
    _executeActionPopularUnallocCity(civ:CivSimCivDetail,action:CivSimActionPopularUnallocCity):boolean{
        //将指定city的所有工人退回
        let r = false;
        const city = civ.cityMap.get(action.source);
        if(city){
            const nps = this.gridForm.getAreaGrids(action.source,CSC.city.civilingRadius);
            for(const np of nps){
                const area = civ.areaMap.get(np);
                if(area&&area.workerFrom.sameAs(city)){
                    area.workerFrom = null;
                }
            }
            r = true;
        }
        return r;
    }
    _executeActionPopularAlloc(civ:CivSimCivDetail,action:CivSimActionPopularAlloc):boolean{
        //将工人安排到area工作
        let r = false;
        const city = civ.cityMap.get(action.source);
        const area = civ.areaMap.get(action.target);
        if(city&&area){
            let cost = 0;
            const nps = this.gridForm.getAreaGrids(action.source,CSC.city.civilingRadius);
            for(const np of nps){
                const na = civ.areaMap.get(np);
                if(na&&!na.sameAs(area)&&na.workerFrom.sameAs(city)){
                    cost += calcPopularCost(na)|0;
                }
            }
            cost += calcPopularCost(area)|0;
            if(cost<=city.popular){
                area.workerFrom = city;
                r = true;
            }
        }
        return r;
    }
    _executeActionAreaBuild(civ:CivSimCivDetail,action:CivSimActionAreaBuild):boolean{
        //新建或升级area
        let r = false;
        const pos = action.source;
        const area = civ.areaMap.get(pos);
        if(area){
            //已有area
            if(area.level===area.targetLevel){
                //未在升级中，可以升级
                const techLevel = calcTechLevel(civ,true);
                if(area.targetLevel<techLevel){
                    //未突破科技上限，可以升级
                    area.targetLevel++;
                    r = true;
                }
            }
        }
        else{
            //新建area
            const land = this.allLandMap.get(pos);
            if(land&&land.belong.id===civ.id){
                const type = action.type;
                if(type===AT_CITY){
                    //城市
                    r = this._addCity(civ,pos);
                }
                else{
                    //其他功能区
                    this._addArea(civ,pos,type);
                    r = true;
                }
            }
        }
        return r;
    }
    _executeActionAreaSwitch(civ:CivSimCivDetail,action:CivSimActionAreaSwitch):boolean{
        let r = false;
        const area = civ.areaMap.get(action.source);
        if(area&&(area.type===AT_IND||area.type===AT_MILI)){
            (area as CivSimModeArea).mode = action.mode;
            r = true;
        }
        return r;
    }
    _executeActionAreaRemove(civ:CivSimCivDetail,action:CivSimActionAreaRemove):boolean{
        let r = false;
        const pos = action.source;
        const area = civ.areaMap.get(pos);
        if(area){
            if(area.level>0){
                //非0级
                area.level--;
                area.targetLevel = area.level;
            }
            else{
                //0级，移除
                if(area.type===AT_CITY){
                    this._removeCity(civ,pos);
                }
                else{
                    this._removeArea(civ,pos);
                }
            }
            r = true;
        }
        return r;
    }
    _executeActionArmyProduct(civ:CivSimCivDetail,action:CivSimActionArmyProduct):boolean{
        let r = false;
        const pos = action.source;
        const area = civ.areaMap.get(pos);
        if(area&&area.type===AT_MILI){
            (area as CivSimMilitaryArea).producting = true;
            r = true;
        }
        return r;
    }
    _executeActionArmyMove(civ:CivSimCivDetail,action:CivSimActionArmyMove):boolean{
        let r = false;
        const pos = action.source;
        const army = civ.armyMap.get(pos);
        if(army&&army.movable){
            const toPos = action.target;
            const maxDistance = CSC.army.movement[army.level];
            if(this.gridForm.getGridDistance(pos,toPos)<=maxDistance){
                const toArmy = this.allArmyMap.get(toPos);
                const toCity = this.allCityMap.get(toPos);
                if(!toArmy&&(!toCity||toCity.belong.id===civ.id||toCity.wall<1)){
                    //空地，或本方城，或城防为0
                    this._unsetArmy(civ,army);
                    army.position = toPos;
                    this._setArmy(civ,army);
                    army.movable = false;
                    r = true;
                }
                else if(toArmy&&toArmy.belong.id===civ.id){
                    //本方军队
                    const cap = CSC.army.stack-toArmy.amount;
                    if(cap>0){
                        if(army.amount<=cap){
                            //合并
                            toArmy.amount += army.amount;
                            this._unsetArmy(civ,army);
                            toArmy.movable = false;
                        }
                        else{
                            //分裂+合并
                            toArmy.amount = CSC.army.stack;
                            army.amount -= cap;
                            army.movable = false;
                            toArmy.movable = false;
                        }
                        r = true;
                    }
                }
            }
        }
        return r;
    }
    _attackArmyArmy(civ:CivSimCivDetail,army:CivSimArmy,toArmy:CivSimArmy){
        //双方均不在城市：基准值为进攻方战斗力的0.3倍
        //当防守方战斗力不足基准值，则防守方消灭，进攻方无损
        //否则，双方各损失基准值战斗力
        const power = Math.ceil(army.power()*0.3)|0;
        const dta = toArmy.afford(power);
        if(dta>=toArmy.amount){
            this._unsetArmy(toArmy.belong,toArmy);
        }
        else{
            toArmy.amount -= dta;
            const da = army.afford(power);
            army.amount -= da;
            //进攻方剩余0
            if(army.amount<1){
                this._unsetArmy(civ,army);
            }
        }
    }
    _attackArmyCity(civ:CivSimCivDetail,army:CivSimArmy,toArmy:CivSimArmy,toCity:CivSimCity){
        //防守方在城市：城市耐久>0时
        //城市耐久损失进攻方战斗力
        //进攻方损失防守方战斗力的0.1倍或剩余城防中较小的一个
        const power = army.power();
        if(power<=toCity.wall){
            //不足以攻陷
            const afford = army.afford(power*0.1);
            army.amount -= afford;
            toCity.wall -= power;
        }
        else{
            const afford = army.afford(Math.min(power*0.1,toCity.wall));
            army.amount -= afford;
            toCity.wall = 0;
        }
        //进攻方剩余0
        if(army.amount<1){
            this._unsetArmy(civ,army);
        }
    }
    _attackCityArmy(civ:CivSimCivDetail,army:CivSimArmy,toArmy:CivSimArmy){
        //进攻方在城市：城市耐久>0时
        //防守方损失进攻方战斗力的0.3倍
        //进攻方无损
        const power = Math.ceil(army.power()*0.3)|0;
        const dta = toArmy.afford(power);
        if(dta>=toArmy.amount){
            this._unsetArmy(toArmy.belong,toArmy);
        }
        else{
            toArmy.amount -= dta;
        }
    }
    _executeActionArmyAttack(civ:CivSimCivDetail,action:CivSimActionArmyAttack):boolean{
        let r = false;
        const pos = action.source;
        const toPos = action.target;
        const army = civ.armyMap.get(pos);
        if(army){
            const toArmy = this.allArmyMap.get(toPos);
            const toCity = this.allCityMap.get(toPos);
            if(toCity&&toCity.belong.id!==civ.id){
                //敌城
                if(toCity.wall>0){
                    //攻城
                    if(army.cityAttackable){
                        this._attackArmyCity(civ,army,toArmy,toCity);
                        army.cityAttackable = false;
                        r = true;
                    }
                }
                else {
                    //城防为0，按无城处理
                    if(army.armyAttackable){
                        this._attackArmyArmy(civ,army,toArmy);
                        army.armyAttackable = false;
                        r = true;
                    }
                }
            }
            else if(toArmy&&army.armyAttackable){
                const city = civ.cityMap.get(pos);
                if(city&&city.wall>0){
                    this._attackCityArmy(civ,army,toArmy);
                }
                else{
                    this._attackArmyArmy(civ,army,toArmy);
                }
                army.armyAttackable = false;
                r = true;
            }
        }
        return r;
    }
    _executeActionArmyOccupy(civ:CivSimCivDetail,action:CivSimActionArmyOccupy):boolean{
        let r = false;
        const pos = action.source;
        const army = civ.armyMap.get(pos);
        const land = this.allLandMap.get(pos);
        if(army&&land&&land.belong&&land.belong.id!==civ.id){
            //只能占领别人的土地，不能占领无主之地
            this._unsetLand(land.belong,land);
            this._setLand(civ,land);
            r = true;
        }
        return r;
    }
    _executeActionArmyBuildCity(civ:CivSimCivDetail,action:CivSimActionArmyBuildCity):boolean{
        let r = false;
        const pos = action.source;
        const army = civ.armyMap.get(pos);
        const land = this.allLandMap.get(pos);
        if(army&&land&&(!land.belong||land.belong.id===civ.id)){
            //只能在自己的土地上或者无主之地上建城
            r = this._addCity(civ,pos);
            if(r){
                land.belong = civ;
            }
        }
        return r;
    }
    _executeAction(civ:CivSimCivDetail,action:CivSimAction){
        if(action){
            const actions = action instanceof CivSimActionMulti?action.actions:[action];
            for(const a of actions){
                let cr = false;
                switch(a.command){
                    case CMD_POPULAR_UNALLOC_AREA:
                        cr = this._executeActionPopularUnallocArea(civ,a as CivSimActionPopularUnallocArea);
                        break;
                    case CMD_POPULAR_UNALLOC_CITY:
                        cr = this._executeActionPopularUnallocCity(civ,a as CivSimActionPopularUnallocCity);
                        break;
                    case CMD_POPULAR_ALLOC:
                        cr = this._executeActionPopularAlloc(civ,a as CivSimActionPopularAlloc);
                        break;
                    case CMD_AREA_BLD:
                        cr = this._executeActionAreaBuild(civ,a as CivSimActionAreaBuild);
                        break;
                    case CMD_AREA_SWITCH:
                        cr = this._executeActionAreaSwitch(civ,a as CivSimActionAreaSwitch);
                        break;
                    case CMD_AREA_RM:
                        cr = this._executeActionAreaRemove(civ,a as CivSimActionAreaRemove);
                        break;
                    case CMD_ARMY_PROD:
                        cr = this._executeActionArmyProduct(civ,a as CivSimActionArmyProduct);
                        break;
                    case CMD_ARMY_MOV:
                        cr = this._executeActionArmyMove(civ,a as CivSimActionArmyMove);
                        break;
                    case CMD_ARMY_ATK:
                        cr = this._executeActionArmyAttack(civ,a as CivSimActionArmyAttack);
                        break;
                    case CMD_ARMY_OCC:
                        cr = this._executeActionArmyOccupy(civ,a as CivSimActionArmyOccupy);
                        break;
                }
                if(cr){
                    this.lastAction = a;
                    this.lastCiv = civ;
                }
                else{
                    break;
                }
            }
        }
    }
    execute(actionMap: KMap<string, CivSimAction>): void {
        this.lastCiv = null;
        this.lastAction = null;
        if(this.running){
            const civ = this._getCurrCiv();
            if(civ){
                if(this.turnChanged){
                    this.turnChanged = false;
                    this._prepareTurn(civ);
                    this.lastCiv = civ;
                    this.lastAction = ACT_TURN_PREPARE;
                }
                else{
                    const agentId = this.civAgentMap.get(civ.id)!;
                    const action = actionMap.get(agentId);
                    if(!action||action.command===CMD_TURN_END){
                        this.turn++;
                        this.turnChanged = true;
                    }
                    else{
                        this.lastCiv = null;
                        this.lastAction = null;
                        this._executeAction(civ,action);
                    }
                }
            }
            else{
                this.turn++;
                this.turnChanged = true;
            }
        }
    }
    _buildCivView(civ:CivSimCivDetail):CivSimView{
        const view:CivSimView = {
            controlCivs: [civ],
            lastCiv: null,
            lastAction: null,
            otherLandMap: new KKP2DTable<CivSimLand>(),
            otherAreaMap: new KKP2DTable<CivSimArea>(),
            otherArmyMap: new KKP2DTable<CivSimArmy>(),
            otherCityMap: new KKP2DTable<CivSimCity>()
        }
        if(this.lastCiv&&civ.id===this.lastCiv.id){
            view.lastCiv = this.lastCiv;
            view.lastAction = this.lastAction;
        }
        //build sight
        const sset = new KKP2DTable<boolean>();
        for(const map of [civ.landMap,civ.armyMap]){
            map.foreach((pos:KKP2D)=>{
                const ss = this.gridForm.getAreaGrids(pos,civ.sight);
                for(const s of ss){
                    sset.set(s,true);
                }
            });
        }
        //build others
        sset.foreach((pos)=>{
            addOther(pos,this.allLandMap,(id:number)=>id!==civ.id,view.otherLandMap);
            addOther(pos,this.allAreaMap,(id:number)=>id!==civ.id,view.otherAreaMap);
            addOther(pos,this.allArmyMap,(id:number)=>id!==civ.id,view.otherArmyMap);
            addOther(pos,this.allCityMap,(id:number)=>id!==civ.id,view.otherCityMap);
        });
        return view;
    }
    _combineCivView(civIds:number[],civViewMap:KMap<number,CivSimView>):CivSimView{
        const civSet = setFromNumArray(civIds);
        const rview:CivSimView = {
            controlCivs: [],
            lastCiv: null,
            lastAction: null,
            otherLandMap: new KKP2DTable<CivSimLand>(),
            otherAreaMap: new KKP2DTable<CivSimArea>(),
            otherArmyMap: new KKP2DTable<CivSimArmy>(),
            otherCityMap: new KKP2DTable<CivSimCity>()
        }
        for(const civId of civIds){
            const view = civViewMap.get(civId);
            if(view){
                rview.controlCivs.push(view.controlCivs[0]);
                if(!rview.lastCiv){
                    rview.lastCiv = view.lastCiv;
                }
                if(!rview.lastAction){
                    rview.lastAction = view.lastAction;
                }
                addOtherMap(view.otherLandMap,(id:number)=>!civSet.contains(id),rview.otherLandMap);
                addOtherMap(view.otherAreaMap,(id:number)=>!civSet.contains(id),rview.otherAreaMap);
                addOtherMap(view.otherArmyMap,(id:number)=>!civSet.contains(id),rview.otherArmyMap);
                addOtherMap(view.otherCityMap,(id:number)=>!civSet.contains(id),rview.otherCityMap);
            }
        }
        return rview;
    }
    _checkEnd(){
        if(this.allCivMap.size<=1){
            this.running = false;
        }
    }
    generateObserverViewsMap(): KMap<string, CivSimView[]> {
        const omap = new KStrTable<CivSimView[]>();
        if(this.running){
            //build civ view
            const civViewMap = new KNumTable<CivSimView>();
            this.allCivMap.foreach((id,civ)=>{
                const view = this._buildCivView(civ);
                civViewMap.set(id,view);
            });
            //build ob view
            const combinedViewMap = new KStrTable<CivSimView>();
            this.observerMap.foreach((id,ob)=>{
                const civIds = ob.getObservedIds();
                if(civIds.length===1){
                    const view = civViewMap.get(civIds[0]);
                    if(view){
                        omap.set(id,[view]);
                    }
                }
                else if(civIds.length>1){
                    civIds.sort();
                    const key = civIds.join("-");
                    const view = combinedViewMap.computeIfAbsent(key,()=>this._combineCivView(civIds,civViewMap));
                    omap.set(id,[view]);
                }
            });
            //end game
            this._checkEnd();
        }
        return omap;
    }
}
